/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Transformers.Wavelets;

/**
 *
 * @author BeerBrewer
 */
public class HaarWavelet {

    private double informationCoefficient; //this will be the last single point of data = average of all points of original data
    private double[] detailCoefficients; //this array will hold information about how to return the informationCoefficient back to his old data.
        //IMPORTANT: there is also a local field called in the same way, if you want this array, put it like this.detailCoefficients!
    private double[] combined; //combined array of the detailCoefficients(place = 1 to end) and the informationCoefficient(place = 0)
    public HaarWavelet(){

    }

    //this method decomposes a one-dimensional array into one single average point and a list of detail coëfficients
    //problems: entry in detailCoefficients can be null!!!!!
    public double[] transform(double[] totransform, double[] givenDetailCoeffs){
        int informationCoefficients;
        boolean uneven = false;

        if ((totransform.length/2.0) > totransform.length/2){ //here we check if the todecompose array is of uneven length
            informationCoefficients  = (totransform.length/2)+1;
            uneven = true;
        }
        else
            informationCoefficients = totransform.length/2;

        double[] transformed = new double[informationCoefficients];
        double[] detailCoefficients;
        if(uneven)
            detailCoefficients = new double[informationCoefficients-1];
        else
            detailCoefficients = new double[informationCoefficients];

        double avgo = average(totransform[0], totransform[1]);
        double dif0 = difference(avgo, totransform[0]);
        transformed[0] = avgo;
        detailCoefficients[0] = dif0;
        //since i steps in 2, only need to check for i==0, and in other case just i/2
        //so to leave out if statement, left out of for-loop
        for (int i=2; i< totransform.length-1; i+=2){
            double avg = average(totransform[i], totransform[i+1]);
            double dif = difference(avg, totransform[i]); //have to do it only with one, since avg is average of both the numbers
            transformed[i/2] = avg;
            detailCoefficients[i/2] = dif;            
        }
        if (uneven){
            transformed[transformed.length-1] = totransform[totransform.length-1];            
            //the detail coefficient of this one will be null, so in the detailCoefficients array it will be left out!
        }

//        print(transformed);
//        print( detailCoefficients);
        if (informationCoefficients == 1){

            setInformationCoefficient(transformed[0]);
            if (givenDetailCoeffs != null)
                setDetailCoefficients(add(detailCoefficients,givenDetailCoeffs));
            else
                setDetailCoefficients(detailCoefficients);
            setCombined();
            return combined;
        }
        else{
            if (givenDetailCoeffs != null){
//                print(transformed);
//                print(add( detailCoefficients, givenDetailCoeffs));
                return transform(transformed, add(detailCoefficients, givenDetailCoeffs));
            }
            else{
//                print(transformed);
//                print(detailCoefficients);
                return transform(transformed, detailCoefficients);
            }
        }
    }
    
    public double[][] transform(double[][] toTransform){
        int x = toTransform.length;
        int y = toTransform[0].length;
        double rowDecomposed[] = new double[x];
        double rowTransformed[] = new double[x];
        
        double rowTransformedArray[][] = new double[x][y];
        
        double columnDecomposed[] = new double[y];
        double columnTransformed[] = new double[y];
        
        double transformedArray[][] = new double[x][y];
      
           
// Transform for every row
        for(int i =0; i < x; i++){
            for(int j =0; j < y; j++){
                rowDecomposed[j] = toTransform[i][j];
            }
            
            rowTransformed = transform(rowDecomposed, null);
            
            for(int k =0; k < rowTransformed.length; k++){
                rowTransformedArray[i][k] = rowTransformed[k];
            }   
        }
        
// Rows are composed, now tranform every column
        for(int i =0; i < x; i++){
            for(int j =0; j < y; j++){
                columnDecomposed[j] = rowTransformedArray[j][i];
            }
            
            columnTransformed = transform(columnDecomposed, null);
            
            for(int k =0; k < columnTransformed.length; k++){
                transformedArray[k][i] = columnTransformed[k];
            }   
        }
        return transformedArray;
    }
    
    
    public double[] add(double[] x, double[] y){
        double[] z = new double[x.length + y.length];
        for (int i=0; i< x.length; i++)
            z[i] = x[i];
        for (int i=0; i< y.length;i++)
            z[i+x.length] = y[i];
        return z;
    }

    public double average(double x, double y){
        double sum = x+y;
        return (x+y)/2.0;
    }

    public double difference(double x, double y){
        return Math.abs(x-y);
    }

    public void setInformationCoefficient(double ic){
        informationCoefficient = ic;
    }

    public void setDetailCoefficients(double[] dc){
        this.detailCoefficients = dc;
    }

    public void setCombined(){

        combined = new double[detailCoefficients.length+1];
        combined[0] = informationCoefficient;
        for (int i=0; i< detailCoefficients.length; i++){
            combined[i+1] = detailCoefficients[i];
        }
    }

    public double getInformationCoefficient(){
        return informationCoefficient;
    }

    public double[] getDetailCoefficients(){
        return detailCoefficients;
    }

    public double[] getCombined(){
        return combined;
    }

    public void print(double[] toPrint){
        System.out.print("[");
        for(int i=0; i< toPrint.length;i++){
            System.out.print(" "+ toPrint[i] );
        }
        System.out.println("]");
    }

    public void print(double[][] toPrint){
        for (int i=0; i <toPrint.length; i++){            
            print(toPrint[i]);
        }
    }
}
